import 'package:flutter_test/flutter_test.dart';

import 'package:blog/tools/check.dart';

void main() {
  test("CheckTool::isHtmlText", () {
    String str = '''
    Go 程序设计语言
=================

1. 初步认识

   1. Hello， World
   2. 命令行参数获取  os.Args
   3. 多行字符串的切割及格式化输出的替换符
   4. Gif 动画的制作 输出Gif内容 到 os.Stdout 标准输出
   5. 使用 net 包获取网页内容
   6. 并发请求多个网页 wget go func() 语法 ， 使用 chan 传输协程里的数据
   7. web server 网络服务

2. 程序结构
3. 基本数据
    ```
    // 类型零值
    bool -> false
    numbers -> 0
    string -> ""
    pointers -> nil
    slices -> nil
    maps -> nil
    channels -> nil
    functions -> nil
    interfaces -> nil
    ```
4. 复合类型
   1. 数组 表示具有指定容量的相同类型数据集合 [n]Type 
   2. 切片 表示可扩容的相同类型数据集合 []Type
      1. 当切片cap值不变时 切片地址不变 当cap值改变时 切片地址也会发生变化
      2. append 函数的解释 这里需要特别注意的是append 函数生成的数组的cap值 都是2的N次方 同一切片的cap值如果足够扩容的时候返回的切片指针值是一致的 例: 

         ```
         a := make([]int, 3, 4)
         a = append(a, 1, 2, 3) // a {cap:4, len:3, value{1,2,3}}
         b := append(a, 4) // b = {cap:4, len:4, value{1,2,3, 4}}
         c := append(a, 5) // b = {cap:4, len:4, value{1,2,3,5}}, c = {cap:4, len:4, value{1,2,3,5}}
         ```

   3. map 
      1. 排序是无序的 即时是同一个map 上下两次 range出来的顺序也是不一样的。
      2. 所有可以比较的类型都可以作map的键
   4. 结构体
      1. 匿名属性的属性访问可以省略 属性名来访问 但是如果外层有同名属性的话优先访问外层的, 准确来说就是属性名会从外向里找

         ```
         type Point struct{
            x,y int
         }
         type Circle struct {
            Point
            x int
         }

         type wheel struct {
            Circle
            x int
         }

         a := &wheel{Circle{Point{1,2}, 3}, 4}
         fmt.Println(a.x) // 4
         ```
   5. json
      json.Marshal 和 json.Unmarshal 的使用 
      json 标签的使用 `json:"color,omitempty"` color 表示属性转为json时的键 omitempty 表示值是零值或空时不输出
   6. 文本和HTML模板
      {{ .a }}
      {{range .Items}}
      Age:{{ .CreatedAt | daysAge }} // 函数处理
      {{end}}

5. 函数

   1. 函数声明
   2. 递归
   3. 多返回值
   4. 错误
   5. 作为值的函数
   6. 匿名函数
   7. 变参函数 
      1. func(vals ...T) {} vals 为 类型为T的slice
      2. 尽管 ffunc(vals ...int) {} 和 func(vals []int){} 在函数内部vals都是[]int 但两个表达式还不是一个类型的
   8. 延后函数调用  defer 关键词用法
      1. defer 会在函数 return或 panic 之后调用 
      2. 如果在defer 中改变了返回变量的值 则改变返回的值

         ```
         func test(width, height int) (area int) {
            res := 0
            defer func(){
               res *= 2 // 不会改变返回值 但是会改变return res 的值
               area *=2 // 会改变返回值
            }()
            res = width * height
            return res
         }
         ```
   9. 崩溃
   10. 恢复

6. 方法

   1. 方法声明
      1. 可以为同一个包下的任意类型添加方法， 只要类型不是指针类型或接口类型
   2. 带有指针接收器的方法
   3. 包含嵌入结构体的复合类型
   4. 作为值和表达式的方法
   5. 示例：位向量类型
   6. 封装

7. 接口

   1. 作为规约的接口
   2. 接口类型
   3. 接口约定的达成
   4. 使用flag.Value进行标志位分析
   5. 作为值的接口

      ```
      func TestConcurrentIncr(t *testing.T) {
         var buf *bytes.Buffer
         if debug {
            buf = new(bytes.Buffer)
         }
         fmt.Printf("%v %T %p\n", buf, buf, buf) // <nil> *bytes.Buffer 0x0
         fw(buf)
         if debug {
            fmt.Println(buf)
         }

      }

      func fw(writer io.Writer) {
         if writer != nil {
            fmt.Printf("%v %T %p\n", writer, writer, writer) // <nil> *bytes.Buffer 0x0
            // 这里会报空指针错误 用goland 调试时发现 这里 writer 类型 是 {io.Write} nil
            writer.Write([]byte("hello world\n"))
         }
      }
      // 这里writer 是一个接口值 虽然动态值是0 动态类型为*bytes.Buffer 接口只有不包含任何信息的时候才== nil
      ```

   6. 使用 sort.Interface进行排序

      ```
      // 必须满足sort.Interface 接口
      func (s T)Len()int
      func (s T)Swap(i, j)
      func (s T)Less(i, j)bool

      // 使用
      sort.Sort(T)
      ```

   7. http.Handler 接口
   8. 错误接口
   9. 示例：表达式评估器
   10. 类型断言
      b, ok := a.(T) // 
   11. 使用类型断言分辨错误
   12. 使用接口类型断言查询行为
   13. 按类型的程序分支
   14. 示例：基于标记符号的XML解码
   15. 几句建议

8. goroutine 和信道

   1. goroutine
   2. 示例：并发时钟服务器
   3. 示例：并发回声服务器
   4. 信道

      ```
      ch := make (chan int) // 无缓冲通道 发送时会阻塞代码 直到有一方接受这个数据的时候
      ch := make (chan int, 0) // 无缓冲通道
      ch := make(chan int, 3) // 容量为3的通道 容量够发送时不阻塞代码。容量满时会阻塞代码，这时和无缓冲通道的逻辑是一致的。
      // 关闭信道函数
      close(chan)
      // 这里OK 为true 表示从信道中读取到值 为false 表示信道被关闭
      x,ok := <- naturals
      naturals := make(chan int)
      // 这个语法可以一直读取信道值 直到信道被关闭
      for x := range naturals {
         fmt.Println(x)
      }
      ```

      ```
      // 之前一直都没有注意到的close用法
      package main

      import (
         "fmt"
         "sync"
         "time"
      )

      var (
         closeChan = make(chan struct{})
         wg sync.WaitGroup
      )

      func test() {
         ticker := time.NewTicker(time.Second)
         defer func(){
            ticker.Stop()
            wg.Done()
         }()
         for i:=0;true;i++{
            select {
            case <-closeChan:
               fmt.Println("trigger close channel")
               return
            case <-ticker.C:
               fmt.Printf("ticker %v ...\n", i)
            }
         }
      }
      func main() {
         // 1. 测试是否close 也会触发 select事件
         wg.Add(1)
         go test()
         time.Sleep(time.Second* 20)
         close(closeChan)
         wg.Wait()
      }
      ```
      ```
      // 输出结果如下 close 也是可以触发 select 事件 并不是只有向 通道发送数据才会触发
      ticker 0 ...
      ticker 1 ...
      ticker 2 ...
      ticker 3 ...
      ticker 4 ...
      ticker 5 ...
      ticker 6 ...
      ticker 7 ...
      ticker 8 ...
      ticker 9 ...
      ticker 10 ...
      ticker 11 ...
      ticker 12 ...
      ticker 13 ...
      ticker 14 ...
      ticker 15 ...
      ticker 16 ...
      ticker 17 ...
      ticker 18 ...
      trigger close channel
      ```
      * 代码可以二次关闭信道 ，但不能向已关闭的信道中发送数据，如果从已关闭的信道中读值会直接读到零值
      ```
      package main

      import (
         "fmt"
         "time"
      )

      func main() {
         ready := make(chan int)
         go func() {
            time.Sleep(time.Second)
            close(ready)
         }()
         // 首次等待关闭
         val1:=<-ready
         fmt.Println("首次关闭", val1)
         val2:=<-ready
         fmt.Println("测试二次待发送",val2)

      }
      // 输出如下
      // 首次关闭 0
      // 测试二次待发送 0
      ```

   5. 并行循环
   6. 示例：并发Web爬虫
   7. 使用 select 实现多工
   8. 示例：并发目录遍历
   9. 取消执行
   10. 示例：聊天服务器
9. 使用共享变量实现并发

   1. 竞险

      函数并发调用时不工作的原因很多， 包括但不限于 死锁 活锁 以及资源耗尽

      活锁（livelock） 比如多个线程在尝试绕开死锁， 却由于过分同步导致反复冲突
      
      死锁： 获取不到资源就等待
      
      活锁： 获取不到资源就一直重试

   2. 互斥量：sync.Mutex
   3. 读写互斥量：sync.RWMutex
   4. 内存同步

      ```
      var x, y int
      go func(){
         x = 1
         fmt.Printf("y", y ,"")
      }()

      go func(){
         y = 1
         fmt.Printf("x", x ,"")
      }()

      // 输出结果可能如下 也是预期结果
      x 0 y 1
      x 1 y 1
      y 0 x 1
      y 1 x 1
      // 但有些情况下可能会出现 预期之外的结果
      x 0 y 0
      ```

      * 对于最后一种情况我们来对协程进行一下拆解

      goroutine A x=1  cpu A x=1 y=0  内存y=0 x=0 这时CPU A缓存未刷回内存 print y=0

      goroutine B y=1  cpu B x=0 y=1  内存y=0 x=0 这时CPU B缓存未刷回内存 print x=0

      最后输出的结果 可能就是 x=0 y=0 解决这个问题可以让两个goroutine 在同一个CPU内执行

      * 还有一个原因是因为编译器的问题
      编译认为 print 的变量 和改变值的变量不是同一个互不干扰 那他们的执行顺序就不是固定的 print 可能会在赋值之前执行

      * 但是我没有模拟出来～


   5. 缓式初始化：sync.Once
      * 这种方式有点像缓存， 原意是希望降低启动时间，在需要时加载数据并初始化变量。但可能会造成类似缓存穿透的情况
      * 解决方法：
      1. 加读写锁 延迟读取操作 该方法需要自己写 可能会容易出错
      2. sync.Once  golang sync 提供的一次加载解决方案
         ```
         var loadOne sync.Once
         loadOne.Do(f func())
         ```

   6. 竞险检测器
      ```
      // test.go
      package main

      import (
         "fmt"
         "sync"
      )

      var balance int

      func Deposit(amount int)  { balance += amount }
      func Withdraw(amount int) { balance -= amount }
      func Balance()int{ return balance}
      func main() {
         var wg sync.WaitGroup
         for i:=0; i< 10; i++ {
            wg.Add(3)
            go func() {Deposit(100);wg.Done()}()
            go func(){ Withdraw(100);wg.Done()}()
            go func() {fmt.Println(Balance());wg.Done()}()
         }
         wg.Wait()
      }

      ```
      `go run -race test.go` 虽然确实观测到竞态结果了，但是结果和不加 -race 的情况下差异太大了
      ```
      ==================
      WARNING: DATA RACE
      Read at 0x000001275ce0 by goroutine 8:
      main.Withdraw()
            /Users/worry/www/test/test.go:11 +0x3e
      main.main.func2()
            /Users/worry/www/test/test.go:18 +0x21

      Previous write at 0x000001275ce0 by goroutine 7:
      main.Deposit()
            /Users/worry/www/test/test.go:10 +0x5a
      main.main.func1()
            /Users/worry/www/test/test.go:17 +0x21

      Goroutine 8 (running) created at:
      main.main()
            /Users/worry/www/test/test.go:18 +0xd0

      Goroutine 7 (finished) created at:
      main.main()
            /Users/worry/www/test/test.go:17 +0xae
      ==================
      ==================
      WARNING: DATA RACE
      Read at 0x000001275ce0 by goroutine 9:
      main.main.func3()
            /Users/worry/www/test/test.go:19 +0x3e

      Previous write at 0x000001275ce0 by goroutine 7:
      main.Deposit()
            /Users/worry/www/test/test.go:10 +0x5a
      main.main.func1()
            /Users/worry/www/test/test.go:17 +0x21

      Goroutine 9 (running) created at:
      main.main()
            /Users/worry/www/test/test.go:19 +0xf2

      Goroutine 7 (finished) created at:
      main.main()
            /Users/worry/www/test/test.go:17 +0xae
      ==================
      100
      0
      0
      0
      0
      0
      0
      0
      0
      0
      Found 2 data race(s)
      ```

   7. 示例：并发无阻塞式高速缓存
   8. goroutine 和线程

10. 包和go工具

    1. 概述
    2. 导入路径
    3. 包声明
    4. 导入声明
    5. 空导入
    6. 包和命名
    7. go 工具
    
11. 测试

    1. go test 工具
    2. 测试函数
    3. 覆盖率
    4. 性能基准函数
    5. 性能剖析
    6. 示例函数

12. 反射

    1. 为什么会引入反射概念
    2. reflect.Type 和 reflect.Value
    3. 递归值输出 Display
    4. 示例：对S-表达式编码
    5. 使用reflect.Value 给变量设置值
    6. 示例：对S-表达式解码
    7. 访问结构体域标签
    8. 列出类型的方法
    9. 几句忠告

13. 低阶程序设计

    1. unsafe.Sizeof, Alignof和Offsetof
    2. unsafe.Pointer
    3. 示例：深等价
    4. 使用cgo 调用C 代码
    5. 再来几句忠告

    ''';
    print(CheckTool.isHtmlText(str));
  });
}
